﻿/*
 * copyright (c) 2009 hvbird
 *
 * This file is part of tdlib.
 *
 * tdlib is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * tdlib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

using System;
using System.IO;
using System.Security;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Text;

namespace TDLib.Security
{
    /// <summary>
    /// 文件加密解密类
    /// </summary>
    public class SecurityFile
    {
        private DecryptTransformer Dec_Transformer; //解密转换器 
        private EncryptTransformer Enc_Transformer; //加密转换器 
        private byte[] initVec;
        private byte[] encKey;

        /// <summary>
        /// 初始化实例
        /// </summary>
        /// <param name="algId"></param>
        public SecurityFile(EncryptionAlgorithm algId)
        {
            Dec_Transformer = new DecryptTransformer(algId);
            Enc_Transformer = new EncryptTransformer(algId);
        }

        /// <summary>
        /// 获取或设置加密的偏移向量
        /// </summary>
        public byte[] IV
        {
            get { return initVec; }
            set { initVec = value; }
        }

        /// <summary>
        /// 获取或设置加密的密钥
        /// </summary>
        public byte[] Key
        {
            get { return encKey; }
            set { encKey = value; }
        }

        /// <summary>
        /// 加密文件 
        /// </summary>
        /// <param name="inFileName">待加密的文件的文件名</param>
        /// <param name="outFileName">输出的文件名</param>
        /// <param name="bytekey">密钥</param>
        /// <param name="bytesIV">偏移量</param>
        public void EncryptFile(string inFileName, string outFileName, byte[] bytekey, byte[] bytesIV)
        {
            try
            {
                using (FileStream fin = new FileStream(inFileName, FileMode.Open, FileAccess.Read))
                {
                    using (FileStream fout = new FileStream(outFileName, FileMode.OpenOrCreate, FileAccess.Write))
                    {
                        fout.SetLength(0);

                        //Create variables to help with read and write. 
                        byte[] bin = new byte[100]; //This is intermediate storage for the encryption. 
                        long rdlen = 0; //This is the total number of bytes written. 
                        long totlen = fin.Length; //This is the total length of the input file. 
                        int len; //This is the number of bytes to be written at a time. 

                        Enc_Transformer.IV = bytesIV;
                        Enc_Transformer.Key = bytekey;

                        using (ICryptoTransform transform = Enc_Transformer.GetCryptoServiceProvider(bytekey))
                        {
                            using (CryptoStream encStream = new CryptoStream(fout, transform, CryptoStreamMode.Write))
                            {

                                //Read from the input file, then encrypt and write to the output file. 
                                while (rdlen < totlen)
                                {
                                    len = fin.Read(bin, 0, 100);
                                    encStream.Write(bin, 0, len);
                                    rdlen = rdlen + len;
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception("在文件加密的时候出现错误！错误提示： \n" + ex.Message);
            }
        }

        /// <summary>
        /// 解密文件
        /// </summary>
        /// <param name="inFileName">待加密的文件的文件名</param>
        /// <param name="outFileName">输出的文件名</param>
        /// <param name="bytekey">密钥</param>
        /// <param name="bytesIV">偏移量</param>
        public void DecryptFile(string inFileName, string outFileName, byte[] bytekey, byte[] bytesIV)
        {
            try
            {
                using (FileStream fin = new FileStream(inFileName, FileMode.Open, FileAccess.Read))
                {
                    using (FileStream fout = new FileStream(outFileName, FileMode.OpenOrCreate, FileAccess.Write))
                    {
                        fout.SetLength(0);

                        //Create variables to help with read and write. 
                        byte[] bin = new byte[100]; //This is intermediate storage for the encryption. 
                        long rdlen = 0; //This is the total number of bytes written. 
                        long totlen = fin.Length; //This is the total length of the input file. 
                        int len; //This is the number of bytes to be written at a time. 

                        Dec_Transformer.IV = bytesIV;
                        Dec_Transformer.Key = bytekey;

                        using (ICryptoTransform transform = Dec_Transformer.GetCryptoServiceProvider(bytekey))
                        {
                            using (CryptoStream encStream = new CryptoStream(fout, transform, CryptoStreamMode.Write))
                            {
                                //Read from the input file, then encrypt and write to the output file. 
                                while (rdlen < totlen)
                                {
                                    len = fin.Read(bin, 0, 100);
                                    encStream.Write(bin, 0, len);
                                    rdlen = rdlen + len;
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception("在文件加密的时候出现错误！错误提示： \n" + ex.Message);
            }
        }
    }

}
